/*
 * json_utilits.hpp
 *
 *  Created on: 2021年10月15日
 *      Author: guyadong
 */

#ifndef COMMON_SOURCE_CPP_JSON_UTILITS_HPP_
#define COMMON_SOURCE_CPP_JSON_UTILITS_HPP_
#include <regex>
#include <mutex>
#include "base64.hpp"
#include "nlohmann/json.hpp"
#ifndef _BASIC_JSON_TPL_PARAMS_

#define _BASIC_JSON_TPL_PARAMS_                                 \
    ObjectType, ArrayType, StringType, BooleanType,             \
    NumberIntegerType, NumberUnsignedType, NumberFloatType,     \
    AllocatorType, JSONSerializer, BinaryType

#endif // !_BASIC_JSON_TPL_PARAMS_

#ifndef _BASIC_JSON_TPL_PARAM_DECL_

#define _BASIC_JSON_TPL_PARAM_DECL_                                                     \
	template<typename U, typename V, typename... Args> class ObjectType = std::map,        \
	template<typename U, typename... Args> class ArrayType = std::vector,                  \
	class StringType = std::string, class BooleanType = bool,                              \
	class NumberIntegerType = std::int64_t,                                                \
	class NumberUnsignedType = std::uint64_t,                                              \
	class NumberFloatType = double,                                                        \
	template<typename U> class AllocatorType = std::allocator,                             \
	template<typename T, typename SFINAE = void> class JSONSerializer = nlohmann::adl_serializer,    \
	class BinaryType = std::vector<std::uint8_t>

#endif // !_BASIC_JSON_TPL_PARAM_DECL_


#ifndef _BASIC_JSON_TPL_DECLARATION_
#define _BASIC_JSON_TPL_DECLARATION_ template<_BASIC_JSON_TPL_PARAM_DECL_>
#endif // !_BASIC_JSON_TPL_DECLARATION_

#ifndef  _BASIC_JSON_TPL_
#define _BASIC_JSON_TPL_                                           \
    nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType,   \
    NumberIntegerType, NumberUnsignedType, NumberFloatType,                \
    AllocatorType, JSONSerializer, BinaryType>

#endif // ! _BASIC_JSON_TPL_

namespace json_utilits{
	template<typename VTYPE, _BASIC_JSON_TPL_PARAM_DECL_>
	VTYPE get_value(const _BASIC_JSON_TPL_ & params, const std::string& name, const VTYPE & defaultValue)
	{
		if (params.contains(name))
		{
			return params[name].template get<VTYPE>();
		}
		return defaultValue;
	}
	_BASIC_JSON_TPL_DECLARATION_
	_BASIC_JSON_TPL_ get_value(const _BASIC_JSON_TPL_ & params, const std::string& name, const _BASIC_JSON_TPL_ & defaultValue)
	{
		if (params.contains(name))
		{
			return params[name];
		}
		return defaultValue;
	}
	/**
	 * 如果value不为空保存到params中 name 指定字段
	 */
	_BASIC_JSON_TPL_DECLARATION_
	void set_value_noempty(_BASIC_JSON_TPL_ & params, const std::string& name, const std::string & value)
	{
		if (!value.empty())
		{
			params[name] = value;
		}
	}

	/**
	 * 如果value大于0保存到params中 name 指定字段
	 */
	template<typename VTYPE, _BASIC_JSON_TPL_PARAM_DECL_>
	typename std::enable_if<std::is_arithmetic<VTYPE>::value,void>::type
	set_value_gt0(_BASIC_JSON_TPL_ & params, const std::string& name, VTYPE value)
	{
		if (value > 0)
		{
			params[name] = value;
		}
	}

	/**
	 * 如果value不为0保存到params中 name 指定字段
	 */
	template<typename VTYPE, _BASIC_JSON_TPL_PARAM_DECL_>
	typename std::enable_if<std::is_arithmetic<VTYPE>::value, void>::type
	set_value_ne0(_BASIC_JSON_TPL_ & params, const std::string& name, VTYPE value)
	{
		if (value != 0)
		{
			params[name] = value;
		}
	}
	template<typename VTYPE,_BASIC_JSON_TPL_PARAM_DECL_>
	void fill_field(VTYPE& output, const _BASIC_JSON_TPL_ & j, const std::string &field)
	{
		if (j.contains(field))output = j[field].template get<VTYPE>();
	}
	_BASIC_JSON_TPL_DECLARATION_
	void fill_field(_BASIC_JSON_TPL_& output, const _BASIC_JSON_TPL_ & j, const std::string &field)
	{
		if (j.contains(field))output = j[field];
	}
	/**
	 * 返回枚举类型变量的名字字符串
	 * 枚举类型 ETYPE 必须支持 nlohmann::json 的名字序列化和返序列
	 */
	template<typename ETYPE, _BASIC_JSON_TPL_PARAM_DECL_>
	static const std::string& name_of(ETYPE e, const _BASIC_JSON_TPL_ & ununsed = nlohmann::json())
	{
		static std::map<ETYPE, std::string> names;
		static std::mutex name_mutex;
		if (!names.count(e))
		{
			// double check
			std::lock_guard<std::mutex> guard(name_mutex);
			if (!names.count(e))
			{
				_BASIC_JSON_TPL_ j;
				to_json(j, e);
				names[e] = std::regex_replace(j.dump(), std::regex("^\"(.*)\"$"), "$1");
			}
		}
		return names[e];
	}
	/** 将类型为string的BASE64编译的字符串转为解码的string json 对象 */
	_BASIC_JSON_TPL_DECLARATION_
	void from_base64(_BASIC_JSON_TPL_ & j)
	{
		if (j.is_string())
		{
			/** 保存BASE64字符串解码后的二进制数据 */
			j = gdface::base64_decode(j.template get<std::string>());

		}
	}
	/** 将类型为string的json对象中的二进制数据转为BASE64编码的 json 对象 */
	_BASIC_JSON_TPL_DECLARATION_
	_BASIC_JSON_TPL_ to_base64(const _BASIC_JSON_TPL_&j)
	{
		_BASIC_JSON_TPL_ out;
		if (j.is_string())
		{
			auto v = j.template get<std::string>();
			out = gdface::base64_encode(v);
		}
		return out;
	}
	//************************************
	// (递归)返回 path 指定的 json 元素
	// @param    const        json & j
	// @param    const std::string & path 节点路径描述
	//                                    如果 j 中包含名为 path 的子节点则直接返回
	//                                    如果 path 包含'/'且不以'/'开始和结尾,则视为'/'分割的节点名组成的路径描述
	//                                    会递归从左到右递归搜索指定的节点,找到所有的节点则返回最后的节点
	//                                    否则返回 null json
	//                                    例如: person/sex 指定返回名为 person 的 object 节点下的 sex 字段
	// @param    bool                try_parse_string 是否尝试解析string类型为json继续搜索
	//                                    为true时,如果 j 为string类型,尝试将其解析为json,在其中查找子节点
	// @return   nlohmann::basic_json
	//************************************
	_BASIC_JSON_TPL_DECLARATION_
	_BASIC_JSON_TPL_ get_json_by_path(const _BASIC_JSON_TPL_& j, const std::string &path, bool try_parse_string = false)
	{
		if (!j.is_null() && !path.empty()) {
			if (j.contains(path)) {
				/** 如果 j 中包含名为 path 的子节点则直接返回 */
				return j[path];
			}
			auto idx = path.find_first_of('/');
			if (idx > 0 && idx < path.size() - 1) 
			{
				/************************************************************************/
				/* idx > 0 即要求字符串包含'/',但不以'/'开始                            */
				/* idx < path.size() - 1,即要求'/'不是结尾                              */
				/************************************************************************/

				/** 因为后面的逻辑有可能会修改_j,所以这里_j要用指针类型,而不能是引用 */
				const _BASIC_JSON_TPL_* _j = &j;
				_BASIC_JSON_TPL_ parsed;
				if (j.is_string())
				{
					if (!try_parse_string)
					{
						return _BASIC_JSON_TPL_();
					}
					/************************************************************************/
					/* 尝试将string解析为json继续查找子节点                                 */
					/* 解析失败返回 null json                                               */
					/************************************************************************/
					try
					{
						parsed = _BASIC_JSON_TPL_::parse(j.template get<std::string>());
						_j = &parsed;
					}
					catch (const std::exception&)
					{
						return _BASIC_JSON_TPL_();
					}
				}
				/** 左侧第一个节点名 */
				auto first = std::string(path.begin(), path.begin() + idx);
				/** 剩余节点名 */
				auto remaining = std::string(path.begin() + idx + 1, path.end());
				if (_j->contains(first))
				{
					/** 递归查找下一级节点 */
					return get_json_by_path((*_j)[first], remaining, try_parse_string);
				}
			}
		}
		// null json
		return _BASIC_JSON_TPL_();
	}
} /** namespace json_utilits */
#endif /* COMMON_SOURCE_CPP_JSON_UTILITS_HPP_ */
